%%%-------------------------------------------------------------------
%%% @author mirahs(2892727493@qq.com)
%%% @doc
%%%     网络通讯协议处理
%%% @end
%%%-------------------------------------------------------------------
-module(net_protocol_handler_tpl).

-export([
    head_size/0

    ,on_accept/1
    ,on_head/1
    ,on_packet/3
    ,on_exit/1
    ,on_close/2
]).

-include("common.hrl").
-include("net.hrl").
-include("env.hrl").


%%%===================================================================
%%% API functions
%%%===================================================================

head_size() ->
    4.

on_accept(Socket) ->
    case gen_tcp:recv(Socket, 23, 10000) of
        {ok, ?NET_CLIENT_GAME} ->
            create_conn(game, Socket);
        {ok, ?NET_CLIENT_WEB} ->
            web_handle(Socket);
        {ok, ?NET_CLIENT_TESTER} ->
            create_conn(tester, Socket);
        {error, Reason} ->
            Peer = inet:peernames(Socket),
            Sock = inet:socknames(Socket),
            ?ERR("获取客户端类型失败 Reaspon:~w,Peer:~w,Sock:~w", [Reason, Peer, Sock]),
            gen_tcp:close(Socket)
    end.

on_head(<<BodyLen:16/big-integer-unsigned, _Cmd:16/big-integer-unsigned>>) ->
    {ok, BodyLen};
on_head(_Bin) ->
    {error, head_data_error}.

on_packet(<<_BodyLen:16/big-integer-unsigned, Cmd:16/big-integer-unsigned>>, BodyBin, State) ->
    routing(Cmd, BodyBin, State).

%% 控制对象异常退出
on_exit(ObjectId) ->
    ?DEBUG("on_exit:~w", [ObjectId]),
    ObjectId.

%% 网络连接退出
on_close(_Object, _ObjectPid) ->
    ?DEBUG("on_close:~w", [_ObjectPid]),
    ok.


%%%===================================================================
%%% Internal functions
%%%===================================================================

create_conn(ClientType, Socket) ->
    {ok, Pid} = net_tcp_conn:start(?MODULE, ClientType, Socket),
    ok = gen_tcp:controlling_process(Socket, Pid),
    ?DEBUG("建立新连接(~w)", [ClientType]).

web_handle(Socket) ->
    case gen_tcp:recv(Socket, 2, 3000) of
        {ok, <<Len:16/big-integer-unsigned>>} ->
            case gen_tcp:recv(Socket, Len, 5000) of
                {ok, BinData} ->
                    ?DEBUG("web_handle 客户端发送过来的数据:~ts", [BinData]),
                    try
                        {ok, {Module, Func, Args, Time, Sign}} = util_type:bitstring_to_term(BinData),

                        Handler = env_mgr:get(?ENV_NET_WEB_HANDLER),
                        Result = Handler:handle(Module, Func, Args, Time, Sign),
                        LenBin = byte_size(Result),
                        gen_tcp:send(Socket, <<LenBin:16/big-integer-unsigned, Result/binary>>)
                    catch _Type:_Reason:Stacktrace ->
                        ?ERR("call_data invalid Type:~w,Reason:~w,Stacktrace:~n~p", [_Type, _Reason, Stacktrace]),
                        Result2 = <<"{\"code\":1, \"msg\":\"call_data invalid\"}">>,
                        LenBin2 = byte_size(Result2),
                        gen_tcp:send(Socket, <<LenBin2:16/big-integer-unsigned, Result2/binary>>)
                    end;
                {error, Reason} ->
                    ?ERR("web_handle 读取包体失败:~w", [Reason])
            end;
        Other ->
            ?ERR("web_handle 读取包头失败[~p]", [Other])
    end,
    gen_tcp:close(Socket).


routing(Cmd = 1001, BodyBin = <<Len:16/big-integer-unsigned, Name:Len/binary, Age:8/big-integer-unsigned>>, State) ->
    ?DEBUG("server routing 1001 name:~ts,age:~w", [Name, Age]),
    util_net:send(erlang:self(), packet_msg(Cmd, BodyBin)),
    {next, State};
routing(Cmd = 1002, BodyBin = <<Gold:16/big-integer-unsigned, Diamond:16/big-integer-unsigned>>, State) ->
    ?DEBUG("server routing 1002 Gold:~w,Diamond:~w", [Gold, Diamond]),
    util_net:send(erlang:self(), [packet_msg(Cmd, BodyBin), packet_msg(Cmd, BodyBin)]),
    {next, State}.


packet_msg(MsgId, BinData) ->
    LenBin	= byte_size(BinData),
    Len		= LenBin + 2,
    <<Len:16/big-integer-unsigned, MsgId:16/big-integer-unsigned, BinData/binary>>.
